#include "HTTPRequest.hpp"

HTTPRequest::HTTPRequest()
{
  m_curl = curl_easy_init();
  if (m_curl != NULL)
  {
    curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, &HTTPRequest::WriteCallback);
    curl_easy_setopt(m_curl, CURLOPT_HEADERFUNCTION, &HTTPRequest::HeaderCallback);
  }

  m_header = NULL;
}

HTTPRequest::~HTTPRequest()
{
  if (m_curl != NULL)
    curl_easy_cleanup(m_curl);

  m_header = NULL;
}

bool HTTPRequest::Initialized()
{
  return (m_curl != NULL);
}

void HTTPRequest::Get(string url, HTTPREQUEST_DATA* buffer, HTTPREQUEST_DATA* header)
{
  curl_easy_setopt(m_curl, CURLOPT_POST, 0);

  Send(url, buffer, header); 
}

void HTTPRequest::Post(string url, HTTPREQUEST_POST& data, HTTPREQUEST_DATA* buffer, HTTPREQUEST_DATA* header)
{
  vector<string> postData;
  for (map<string, string>::iterator i = data.begin(); i != data.end(); i++)
    postData.push_back(i->first + "=" + i->second);

  // set options
  curl_easy_setopt(m_curl, CURLOPT_COPYPOSTFIELDS, join(postData, "&").c_str());
  curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, NULL);
  curl_easy_setopt(m_curl, CURLOPT_READDATA, NULL);
  
  Send(url, buffer, header);
}

void HTTPRequest::Post(string url, HTTPREQUEST_DATA& data, HTTPREQUEST_DATA* buffer, HTTPREQUEST_DATA* header)
{
  ReadDataIterators* iterators = new ReadDataIterators();
  
  iterators->m_current = data.begin();
  iterators->m_end = data.end();

  // enable read callback
  curl_easy_setopt(m_curl, CURLOPT_POST, 1L);
  curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, &HTTPRequest::ReadCallback);
  curl_easy_setopt(m_curl, CURLOPT_READDATA, iterators);
  curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, data.size());
  
  Send(url, buffer, header);
}

void HTTPRequest::SetProgressCallback(PROGRESS_CALLBACK callback)
{
  curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, false);
  curl_easy_setopt(m_curl, CURLOPT_PROGRESSFUNCTION, callback);
}

size_t HTTPRequest::WriteCallback(void* data, size_t size, size_t members, vector<unsigned char>* buffer)
{
  char* castedData = reinterpret_cast<char*>(data);

  for (unsigned int i = 0; i < size * members; i++)
    buffer->push_back(castedData[i]);

  return size * members;
}

size_t HTTPRequest::ReadCallback(void* data, size_t size, size_t members, ReadDataIterators* iterators)
{
  if (size * members < 1 || iterators->m_current == iterators->m_end)
  {
    delete iterators;
    return 0;
  }

  reinterpret_cast<char*>(data)[0] = *iterators->m_current;
  advance(iterators->m_current, 1);
  return 1;
}

size_t HTTPRequest::HeaderCallback(void* data, size_t size, size_t members, HTTPREQUEST_DATA* buffer)
{
  char* castedData = reinterpret_cast<char*>(data);

  for (unsigned int i = 0; i < size * members; i++)
    buffer->push_back(castedData[i]);

  return size * members;
}

void HTTPRequest::Send(string url, HTTPREQUEST_DATA* buffer, HTTPREQUEST_DATA* header)
{
  curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
  curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, buffer);
  curl_easy_setopt(m_curl, CURLOPT_WRITEHEADER, header);

  if (m_header != NULL)
    curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_header);

  curl_easy_perform(m_curl);
}

void HTTPRequest::AddHeader(string pair)
{
  m_header = curl_slist_append(m_header, pair.c_str());
}

void HTTPRequest::ClearHeaders()
{
  curl_slist_free_all(m_header);
  m_header = NULL;
}

void HTTPRequest::SetTimeout(int timeout)
{
  curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, timeout);
}

bool HTTPRequest::GlobalInitialize()
{
  return (curl_global_init(CURL_GLOBAL_ALL) == 0);
}

string HTTPRequest::Escape(string text)
{
  char* encoded = curl_easy_escape(m_curl, text.c_str(), text.length());

  string ret(encoded);
  curl_free(encoded);
  return ret;
}